package com.nifostasky.main.thread.lock;

/**
 *
 */
public class SynchronizedLock {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = null;
        Thread t2 = null;
        Thread t3 = null;

        System.out.println("==========形式一:同步代码块===============================================================================================================");
        /**
         * 形式一:
         * 同步代码块
         */
        CodeBlockThread CodeBlockThread = new CodeBlockThread();
        t1 = new Thread(CodeBlockThread);
        t2 = new Thread(CodeBlockThread);
        t3 = new Thread(CodeBlockThread);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println("==========结束===============================================================================================================");
        System.out.println("==========形式二:同步方法===============================================================================================================");
        MethodThread MethodThread = new MethodThread();
        t1 = new Thread(MethodThread);
        t2 = new Thread(MethodThread);
        t3 = new Thread(MethodThread);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println("==========结束===============================================================================================================");
        System.out.println("==========形式三:同步静态方法===============================================================================================================");
        StaticMethodThread StaticMethodThread = new StaticMethodThread();
        t1 = new Thread(StaticMethodThread);
        t2 = new Thread(StaticMethodThread);
        t3 = new Thread(StaticMethodThread);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println("==========结束===============================================================================================================");


    }

}

/**
 * 同步代码块
 * 同步监视器,可以使用this, CodeBlockThread.class , obj公用对象
 */
class CodeBlockThread implements Runnable {
    private int tiketsNum = 100;

    //由于，Runnable实现多线程，所有线程共用一个实现类的对象，所以三个线程都共用实现类中的这个Object类的对象。
    //所以这个对象仅仅只是用于充当 同步作用的属性; 没有其他什么实际含义
    Object obj = new Object();


    //如果时继承Thread类实现多线程，那么需要使用到static Object obj = new Object();
    public void run() {
        while (true) {
            //这个同步监视器,可以使用this, CodeBlockThread.class , obj公用对象
            synchronized (obj) {
                if (tiketsNum > 0) {
                    try {
                        //手动让线程进入阻塞,增大安全性发生的概率
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":\t票号:" + tiketsNum + "\t剩余票数:" + --tiketsNum);
                } else {
                    break;
                }
            }
        }
    }
}


/**
 * 同步方法
 */
class MethodThread implements Runnable {
    private int tiketsNum = 100;

    @Override
    public void run() {
        while (tiketsNum > 0) {
            show();
        }
    }

    //此种方式适合只创建了 一个本 Runable对象 但为这个runable对象创建了多个线程 的形式;
    //同步监视器：this,本示例对象
    private synchronized void show() {
        if (tiketsNum > 0) {
            try {
                //手动让线程进入阻塞,增大安全性发生的概率
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":\t票号:" + tiketsNum + "\t剩余票数:" + --tiketsNum);
        }
    }
}


/**
 * 同步静态方法
 */
class StaticMethodThread implements Runnable {
    public static int tiketsNum = 100;

    @Override
    public void run() {
        while (tiketsNum > 0) {
            show();
        }
    }

    //同步监视器：StaticMethodThread.class  不加static话同步监视器为t1 t2 t3所以错误
    //通过反射入口进行同步,此种方式适合创建了 多个Runable对象,同时需要多个不同的Runable对象进行同步时使用;
    public static synchronized void show() {
        if (tiketsNum > 0) {
            try {
                //手动让线程进入阻塞,增大安全性发生的概率
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":\t票号:" + tiketsNum + "\t剩余票数:" + --tiketsNum);
        }
    }
}




/**
 * 同步监视器 可以为 : 反射入口, this本实例化对象, 共用对象Object;
 *
 *  synchronized(同步监视器){需要被同步的代码}
 *
 * 1. **需要被同步的代码**:操作共享数据（多个线程共同操作的变量）的代码, 不能多包涵代码（效率低，如果包到while前面就变成了单线程了），也不能少包含代码
 * 2. **共享数据**：多个线程共同操作的变量。
 * 3. **同步监视器**：俗称，锁。任何一个类的对象都可以充当锁。但是所有的线程都必须共用一把锁，共用一个对象。
 *
 * 锁的选择：
 *
 * 1. 自行创建，共用对象，如下面demo中的Object对象。
 *
 * 2. 使用this表示当前类的对象
 *
 *    继承Thread的方法中的锁不能使用this代替，因为继承thread实现多线程时，会创建多个子类对象(相当于每个线程一个当前类对象)来代表多个线程，这个时候this指的时当前这个类的多个对象，不唯一，无法当作锁。
 *
 *    实现Runnable接口的方式中，this可以当作锁，因为这种方式只需要创建一个实现类的对象(多个线程共享一个对象)，将实现类的对象传递给多个Thread类对象来当作多个线程，this就是这个一个实现类的对象，是唯一的，被所有线程所共用的对象。
 *
 * 3. 反射入口当作锁，以下面demo为例，其中的锁可以写为`WindowThread.class`
 *
 * 优点：同步的方式，解决了线程安全的问题
 *
 * 缺点：操作同步代码时，只能有一个线程参与，其他线程等待。相当于时一个单线程的过程，效率低。
 */